home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / newsrc.c < prev    next >
C/C++ Source or Header  |  1996-04-08  |  15KB  |  428 lines

  1. /*
  2.  * Program:    Newsrc manipulation routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    12 September 1994
  13.  * Last Edited:    8 April 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include "misc.h"
  42. #include "newsrc.h"
  43.  
  44. /* Error message
  45.  * Accepts: message format
  46.  *        additional message string
  47.  *        message level
  48.  * Returns: NIL, always
  49.  */
  50.  
  51. static long newsrc_error (char *fmt,char *text,long errflg)
  52. {
  53.   char tmp[MAILTMPLEN];
  54.   sprintf (tmp,fmt,text);
  55.   mm_log (tmp,errflg);
  56.   return NIL;
  57. }
  58.  
  59.  
  60. /* Write error message
  61.  * Accepts: newsrc name
  62.  *        file designator
  63.  *        file designator
  64.  * Returns: NIL, always
  65.  */
  66.  
  67. static long newsrc_write_error (char *name,FILE *f1,FILE *f2)
  68. {
  69.   fclose (f1);            /* close file designators */
  70.   fclose (f2);
  71.   return newsrc_error ("Error writing to %s",name,ERROR);
  72. }
  73.  
  74.  
  75. /* Create newsrc file in local form
  76.  * Accepts: notification flag
  77.  * Returns: file designator of newsrc
  78.  */
  79.  
  80. static FILE *newsrc_create (int notify)
  81. {
  82.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  83.   FILE *f = fopen (newsrc,"wb");
  84.   if (!f) newsrc_error ("Unable to create news state %s",newsrc,ERROR);
  85.   else if (notify) newsrc_error ("Creating news state %s",newsrc,WARN);
  86.   return f;
  87. }
  88.  
  89. /* Write new state in newsrc
  90.  * Accepts: file designator of newsrc
  91.  *        group
  92.  *        new subscription status character
  93.  *        newline convention
  94.  * Returns: T if successful, NIL otherwise
  95.  */
  96.  
  97. static long newsrc_newstate (FILE *f,char *group,char state,char *nl)
  98. {
  99.   return (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
  100.       ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF) &&
  101.       (fclose (f) != EOF)) ? LONGT : NIL;
  102. }
  103.  
  104.  
  105. /* Write messages in newsrc
  106.  * Accepts: file designator of newsrc
  107.  *        MAIL stream
  108.  *        message number/newsgroup message map
  109.  *        newline convention
  110.  * Returns: T if successful, NIL otherwise
  111.  */
  112.  
  113. static long newsrc_newmessages (FILE *f,MAILSTREAM *stream,
  114.                 unsigned long *number,char *nl)
  115. {
  116.   unsigned long i,j,k;
  117.   char tmp[MAILTMPLEN];
  118.   int c = ' ';
  119.   for (i = 0,j = 1,k = 0; i < stream->nmsgs; ++i) {
  120.                 /* deleted message? */
  121.     if (mail_elt (stream,i+1)->deleted) {
  122.       k = number[i];        /* this is the top of the current range */
  123.       if (!j) j = k;        /* if no range in progress, start one */
  124.     }
  125.     else if (j) {        /* unread message, ending a range */
  126.       if (k = number[i] - 1) {    /* calculate end of range */
  127.                 /* dump range */
  128.     sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  129.     if (fputs (tmp,f) == EOF) return NIL;
  130.     c = ',';        /* need a comma after the first time */
  131.       }
  132.       j = 0;            /* no more range in progress */
  133.     }
  134.   }
  135.   if (j) {            /* dump trailing range */
  136.     sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  137.     if (fputs (tmp,f) == EOF) return NIL;
  138.   }
  139.                 /* write trailing newline, return */
  140.   return (fputs (nl,f) == EOF) ? NIL : LONGT;
  141. }
  142.  
  143. /* Find list of newsgroups
  144.  * Accepts: pattern to search
  145.  */
  146.  
  147. void newsrc_find (char *pat)
  148. {
  149.   char *s,tmp[MAILTMPLEN];
  150.   int c = ' ';
  151.   char *grp = tmp;
  152.   FILE *f = fopen ((char *) mail_parameters (NIL,GET_NEWSRC,NIL),"rb");
  153.   if (f) {            /* got file? */
  154.                 /* begin with a host specification? */
  155.     if ((*pat == '{') && (s = strchr (pat,'}'))) {
  156.       strcpy (tmp,pat);        /* copy host name */
  157.       grp = tmp + (s + 1 - pat); /* where we write the bboards */
  158.     }
  159.     while (c != EOF) {        /* yes, read newsrc */
  160.       for (s = grp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  161.        (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
  162.        *s++ = c);
  163.       if (c == ':') {        /* found a subscribed newsgroup? */
  164.     *s = '\0';        /* yes, tie off name */
  165.                 /* report if match */
  166.     if (pmatch (tmp,pat)) mm_bboard (tmp);
  167.       }
  168.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  169.     }
  170.     fclose (f);
  171.   }
  172. }
  173.  
  174. /* Update subscription status of newsrc
  175.  * Accepts: group
  176.  *        new subscription status character
  177.  * Returns: T if successful, NIL otherwise
  178.  */
  179.  
  180. long newsrc_update (char *group,char state)
  181. {
  182.   char tmp[MAILTMPLEN];
  183.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  184.   long ret = NIL;
  185.   FILE *f = fopen (newsrc,"r+b");
  186.   if (f) {            /* update existing file */
  187.     int c;
  188.     char *s,nl[3];
  189.     long pos = 0;
  190.     nl[0] = nl[1] = nl[2]='\0';    /* no newline known yet */
  191.     do {            /* read newsrc */
  192.       for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  193.        (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
  194.        *s++ = c) pos = ftell (f);
  195.       *s = '\0';        /* tie off name */
  196.                 /* found the newsgroup? */
  197.       if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
  198.     if (c == state) {    /* already at that state? */
  199.       if (c == ':') newsrc_error ("Already subscribed to %s",group,WARN);
  200.       ret = LONGT;        /* noop the update */
  201.     }
  202.                 /* write the character */
  203.     else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
  204.     if (fclose (f) == EOF) ret = NIL;
  205.     f = NIL;        /* done with file */
  206.     break;
  207.       }
  208.                 /* gobble remainder of this line */
  209.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  210.                 /* need to know about newlines and found it? */
  211.       if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
  212.                 /* sniff and see if an LF */
  213.     if ((c = getc (f)) == '\012') nl[1] = c;
  214.     else ungetc (c,f);    /* nope, push it back */
  215.       }
  216.     } while (c != EOF);
  217.  
  218.     if (f) {            /* still haven't written it yet? */
  219.       if (nl[0]) {        /* know its newline convention? */
  220.     fseek (f,0L,2);        /* yes, seek to end of file */
  221.     ret = newsrc_newstate (f,group,state,nl);
  222.       }
  223.       else {            /* can't find a newline convention */
  224.     fclose (f);        /* punt the file */
  225.                 /* can't win if read something */
  226.     if (pos) newsrc_error("Unknown newline convention in %s",newsrc,ERROR);
  227.                 /* file must have been empty, rewrite it */
  228.     else ret = newsrc_newstate (newsrc_create (NIL),group,state,"\n");
  229.       }
  230.     }
  231.   }
  232.                 /* create new file */
  233.   else ret = newsrc_newstate (newsrc_create (T),group,state,"\n");
  234.   return ret;            /* return with update status */
  235. }
  236.  
  237. /* Update newsgroup entry in newsrc
  238.  * Accepts: newsgroup name
  239.  *        MAIL stream
  240.  *        message number/newsgroup message map
  241.  * Returns: number of recent messages
  242.  */
  243.  
  244. long newsrc_read (char *group,MAILSTREAM *stream,unsigned long *number)
  245. {
  246.   int c;
  247.   char *s,tmp[MAILTMPLEN];
  248.   unsigned long i,j;
  249.   MESSAGECACHE *elt;
  250.   long m = 0,recent = 0,unseen = 0;
  251.   FILE *f = fopen ((char *) mail_parameters (NIL,GET_NEWSRC,NIL),"rb");
  252.   if (f) do {            /* read newsrc */
  253.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  254.      (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
  255.     *s = '\0';            /* tie off name */
  256.     if ((c==':') || (c=='!')) {    /* found newsgroup? */
  257.       if (strcmp (tmp,group))    /* group name match? */
  258.     while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  259.       else {            /* yes, skip leading whitespace */
  260.     while ((c = getc (f)) == ' ');
  261.                 /* only if unprocessed messages */
  262.     if (stream->nmsgs) while (f && (m < stream->nmsgs)) {
  263.                 /* collect a number */
  264.       if (isdigit (c)) {    /* better have a number */
  265.         for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
  266.         if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
  267.           j = j*10 +(c-'0');/* collect second value if range */
  268.                 /* skip messages before first value */
  269.         while ((m < stream->nmsgs) && (number[m] < i)) {
  270.           if (!unseen) unseen = m + 1;
  271.           m++;        /* next message */
  272.         }          
  273.                 /* do all messages in range */
  274.         if (j) while ((m < stream->nmsgs) && (number[m] >= i) &&
  275.               (number[m] <= j)) {
  276.           (elt = mail_elt (stream,++m))->valid = T;
  277.           elt->deleted = T;
  278.         }
  279.         else if ((m < stream->nmsgs) && number[m] == i) {
  280.           (elt = mail_elt (stream,++m))->valid = T;
  281.           elt->deleted = T;
  282.         }
  283.       }
  284.  
  285.       switch (c) {        /* what is the delimiter? */
  286.       case ',':        /* more to come */
  287.         c = getc (f);    /* get first character of number */
  288.         break;
  289.       default:        /* bogus character */
  290.         sprintf (tmp,"Bogus character 0x%x in news state",(int) c);
  291.         mm_log (tmp,ERROR);
  292.       case EOF: case '\015': case '\012':
  293.         fclose (f);        /* all done - close the file */
  294.         f = NIL;
  295.         break;
  296.       }
  297.     }
  298.     else {            /* empty newsgroup */
  299.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  300.       fclose (f);        /* all done - close the file */
  301.       f = NIL;
  302.     }
  303.       }
  304.     }
  305.   } while (f && (c != EOF));    /* until file closed or EOF */
  306.   if (f) {            /* still have file open? */
  307.     sprintf (tmp,"No state for newsgroup %s found, reading as new",group);
  308.     mm_log (tmp,WARN);
  309.     fclose (f);            /* close the file */
  310.   }
  311.   while (m < stream->nmsgs){    /* mark all remaining messages as new */
  312.     (elt = mail_elt (stream,++m))->valid = T;
  313.     elt->recent = T;
  314.     ++recent;            /* count another recent message */
  315.   }
  316.   if (unseen) {            /* report first unseen message */
  317.     sprintf (tmp,"[UNSEEN] %ld is first unseen message in %s",unseen,group);
  318.     mm_notify (stream,tmp,(long) NIL);
  319.   }
  320.   return recent;
  321. }
  322.  
  323. /* Update newsgroup entry in newsrc
  324.  * Accepts: newsgroup name
  325.  *        MAIL stream
  326.  *        message number/newsgroup message map
  327.  * Returns: T if successful, NIL otherwise
  328.  */
  329.  
  330. long newsrc_write (char *group,MAILSTREAM *stream,unsigned long *number)
  331. {
  332.   int c,d = EOF;
  333.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  334.   char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
  335.   FILE *f,*bf;
  336.   nl[0] = nl[1] = nl[2] = '\0';    /* no newline known yet */
  337.   if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
  338.     if (!(bf = fopen ((strcat (strcpy (backup,newsrc),".old")),"wb"))) {
  339.       fclose (f);        /* punt input file */
  340.       return newsrc_error ("Can't create backup news state %s",backup,ERROR);
  341.     }
  342.                 /* copy to backup file */
  343.     while ((c = getc (f)) != EOF) {
  344.                 /* need to know about newlines and found it? */
  345.       if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
  346.                 /* sniff and see if an LF */
  347.     if ((c = getc (f)) == '\012') nl[1] = c;
  348.     ungetc (c,f);        /* push it back */
  349.       }
  350.                 /* write to backup file */
  351.       if ((d = putc (c,bf)) == EOF) {
  352.     fclose (f);        /* punt input file */
  353.     return newsrc_error("Error writing backup news state %s",newsrc,ERROR);
  354.       }
  355.     }
  356.     fclose (f);            /* close existing file */
  357.     if (fclose (bf) == EOF)    /* and backup file */
  358.       return newsrc_error ("Error closing backup news state %s",newsrc,ERROR);
  359.     if (d == EOF) {        /* open for write if empty file */
  360.       if (f = newsrc_create (NIL)) bf = NIL;
  361.       else return NIL;
  362.     }
  363.     else if (!nl[0])        /* make sure newlines valid */
  364.       return newsrc_error ("Unknown newline convention in %s",newsrc,ERROR);
  365.                 /* now read backup file */
  366.     else if (!(bf = fopen (backup,"rb")))
  367.       return newsrc_error ("Error reading backup news state %s",backup,ERROR);
  368.                 /* open newsrc for writing */
  369.     else if (!(f = fopen (newsrc,"wb"))) {
  370.       fclose (bf);        /* punt backup */
  371.       return newsrc_error ("Can't rewrite news state %s",newsrc,ERROR);
  372.     }
  373.   }
  374.   else {            /* create new newsrc file */
  375.     if (f = newsrc_create (T)) bf = NIL;
  376.     else return NIL;        /* can't create newsrc */
  377.   }
  378.   
  379.   while (bf) {            /* read newsrc */
  380.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
  381.      (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
  382.     *s = '\0';            /* tie off name */
  383.                 /* saw correct end of group delimiter? */
  384.     if (tmp[0] && ((c == ':') || (c == '!'))) {
  385.                 /* yes, write newsgroup name and delimiter */
  386.       if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
  387.     return newsrc_write_error (newsrc,bf,f);
  388.       if (!strcmp (tmp,group)) {/* found correct group? */
  389.                 /* yes, write new status */
  390.     if (!newsrc_newmessages (f,stream,number,nl[0] ? nl : "\n"))
  391.       return newsrc_write_error (newsrc,bf,f);
  392.                 /* skip past old data */
  393.     while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'));
  394.                 /* skip past newline */
  395.     while ((c == '\015') || (c == '\012')) c = getc (bf);
  396.     while (c != EOF) {    /* copy remainder of file */
  397.       if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  398.       c = getc (bf);    /* get next character */
  399.     }
  400.                 /* done with file */
  401.     if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,f);
  402.     f = NIL;
  403.       }
  404.                 /* copy remainder of line */
  405.       else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
  406.     if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  407.       if (c == '\015') {    /* write CR if seen */
  408.     if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  409.                 /* sniff to see if LF */
  410.     if (((c = getc (bf)) != EOF) && (c != '\012')) ungetc (c,bf);
  411.       }
  412.                 /* write LF if seen */
  413.       if ((c == '\012') && (putc (c,f) == EOF))
  414.     return newsrc_write_error (newsrc,bf,f);
  415.     }
  416.     if (c == EOF) {        /* hit end of file? */
  417.       fclose (bf);        /* yup, close the file */
  418.       bf = NIL;
  419.     }
  420.   }
  421.   if (f) {            /* still have newsrc file open? */
  422.     if ((fputs (group,f) == EOF) || ((putc (':',f)) == EOF) ||
  423.     (!newsrc_newmessages (f,stream,number,nl[0] ? nl : "\n")) ||
  424.     (fclose (f) == EOF)) return newsrc_write_error (newsrc,bf,f);
  425.   }
  426.   return LONGT;
  427. }
  428.